﻿using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System.Text;
using Org.BouncyCastle.Math;

namespace PmSoft.Core.Cryptos;

public class SM2Utility
{
    public static readonly string DEFAULT_ENCODING = "UTF-8";
    private static ECDomainParameters? _domainParams;

    public static ECDomainParameters GetDomainParams()
    {
        if (_domainParams == null)
        {
            BigInteger ecc_p = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16);
            BigInteger ecc_a = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16);
            BigInteger ecc_b = new BigInteger("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16);
            BigInteger ecc_n = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16);
            BigInteger ecc_gx = new BigInteger("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
            BigInteger ecc_gy = new BigInteger("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
            ECCurve curve = new FpCurve(ecc_p, ecc_a, ecc_b, null, null);
            ECPoint curve_g = curve.CreatePoint(ecc_gx, ecc_gy);
            _domainParams = new ECDomainParameters(curve, curve_g, ecc_n, BigInteger.One);
        }
        return _domainParams;
    }

    public static Dictionary<string, string> GenerateKeys()
    {
        Dictionary<string, string> keysMap = new Dictionary<string, string>();
        ECKeyGenerationParameters ekgParams = new ECKeyGenerationParameters(GetDomainParams(), new SecureRandom());
        ECKeyPairGenerator keyGen = new ECKeyPairGenerator();
        keyGen.Init(ekgParams);
        AsymmetricCipherKeyPair keyPair = keyGen.GenerateKeyPair();
        // 获取公钥
        ECPublicKeyParameters pubKey = (ECPublicKeyParameters)keyPair.Public;
        string pubKeyStr = Hex.ToHexString(pubKey.Q.GetEncoded(false));
        keysMap["publicKey"] = pubKeyStr;
        // 获取私钥
        ECPrivateKeyParameters priKey = (ECPrivateKeyParameters)keyPair.Private;
        string priKeyStr = Hex.ToHexString(priKey.D.ToByteArrayUnsigned());
        keysMap["privateKey"] = priKeyStr;
        return keysMap;
    }

    public static string Encrypt(string publicKey, string text)
    {
        ECDomainParameters domainParams = GetDomainParams();
        byte[] bytes = Hex.Decode(publicKey);
        ECPoint point = domainParams.Curve.DecodePoint(bytes);
        ECPublicKeyParameters ecpuKey = new ECPublicKeyParameters(point, domainParams);
        byte[] data = Encoding.GetEncoding(DEFAULT_ENCODING).GetBytes(text);
        SM2Engine engine = new SM2Engine();
        ParametersWithRandom pwr = new ParametersWithRandom(ecpuKey, new SecureRandom());
        engine.Init(true, pwr);
        return Hex.ToHexString(engine.ProcessBlock(data, 0, data.Length));
    }

    public static string Decrypt(string privateKey, string cipherText)
    {
        BigInteger biKey = new BigInteger(1, Hex.Decode(privateKey));
        ECPrivateKeyParameters ecprKey = new ECPrivateKeyParameters(biKey, GetDomainParams());
        byte[] cipherData = Hex.Decode(cipherText);
        SM2Engine engine = new SM2Engine();
        engine.Init(false, ecprKey);
        return Encoding.GetEncoding(DEFAULT_ENCODING).GetString(engine.ProcessBlock(cipherData, 0, cipherData.Length));
    }

    public static string Sign(string privateKey, string withId, string text)
    {
        BigInteger biKey = new BigInteger(1, Hex.Decode(privateKey));
        ECPrivateKeyParameters ecprKey = new ECPrivateKeyParameters(biKey, GetDomainParams());
        byte[] data = Encoding.GetEncoding(DEFAULT_ENCODING).GetBytes(text);
        SM2Signer signer = new SM2Signer();
        ICipherParameters? param = null;
        ParametersWithRandom pwr = new ParametersWithRandom(ecprKey, new SecureRandom());
        if (withId != null)
        {
            param = new ParametersWithID(pwr, Encoding.GetEncoding(DEFAULT_ENCODING).GetBytes(withId));
        }
        else
        {
            param = pwr;
        }
        signer.Init(true, param);
        signer.BlockUpdate(data, 0, data.Length);
        return Hex.ToHexString(signer.GenerateSignature());
    }

    public static bool Verify(string publicKey, string withId, string text, string sign)
    {
        ECDomainParameters domainParams = GetDomainParams();
        ECPoint point = domainParams.Curve.DecodePoint(Hex.Decode(publicKey));
        ECPublicKeyParameters ecpuKey = new ECPublicKeyParameters(point, domainParams);
        byte[] data = Encoding.GetEncoding(DEFAULT_ENCODING).GetBytes(text);
        SM2Signer signer = new SM2Signer();
        ICipherParameters? param = null;
        if (withId != null)
        {
            param = new ParametersWithID(ecpuKey, Encoding.GetEncoding(DEFAULT_ENCODING).GetBytes(withId));
        }
        else
        {
            param = ecpuKey;
        }
        signer.Init(false, param);
        signer.BlockUpdate(data, 0, data.Length);
        return signer.VerifySignature(Hex.Decode(sign));
    }
}
